import { useMemo } from 'react';

import { useMatches, useRouteError } from 'react-router-dom';

import { parseQuery, stringifyQuery, type LocationQueryRaw } from '@/router/tools';

/**
 * 自定义路由钩子，提供增强的路由操作方法
 * @returns 包含路由操作方法的对象
 *
 * @example
 * const router = useRouter();
 * router.push('/users', { id: '123' }); // 带查询参数跳转
 * router.replace('/home'); // 替换当前路由
 */
export function useRouter() {
  const navigate = useNavigate();

  return useMemo(
    () => ({
      /**
       * 返回上一页
       * @example router.back();
       */
      back: () => navigate(-1),

      /**
       * 前进到下一页
       * @example router.forward();
       */
      forward: () => navigate(1),

      /**
       * 刷新当前页面
       * @example router.reload();
       */
      reload: () => window.location.reload(),

      /**
       * 导航到上级路由
       * @example router.navigateUp();
       */
      navigateUp: () => navigate('..'),

      /**
       * 跳转到指定路径（支持查询参数和状态传递）
       * @param path - 目标路径
       * @param query - 查询参数对象
       * @param state - 路由状态对象
       * @param _replace - 是否替换当前路由（默认false）
       *
       * @example
       * // 基本跳转
       * router.push('/users');
       * // 带查询参数
       * router.push('/users', { page: '1', size: '10' });
       * // 带状态和替换当前路由
       * router.push('/profile', null, { from: 'home' }, true);
       */
      push: (
        path: string,
        query?: LocationQueryRaw,
        state?: Record<string, unknown>,
        _replace?: boolean
      ) => {
        let _path = path;

        // 处理查询参数
        if (query) {
          const search = stringifyQuery(query);
          _path = `${path}?${search}`;
        }

        navigate(_path, {
          replace: _replace,
          state,
        });
      },

      /**
       * 导航到指定路径（push的别名方法）
       * @param path - 目标路径
       * @example router.navigate('/dashboard');
       */
      navigate: (path: string) => navigate(path),

      /**
       * 替换当前路由
       * @param path - 目标路径
       * @example router.replace('/login');
       */
      replace: (path: string) => navigate(path, { replace: true }),
    }),
    [navigate]
  );
}
/**
 * 自定义路由钩子，用于获取当前路由信息
 *
 * @template T - 路由数据类型
 * @template Q - 查询参数类型，默认为Record<string, string>
 * @template P - 路径参数类型，默认为Record<string, string | string[]>
 *
 * @returns 返回包含路由信息的增强路由对象，包括：
 *   - 当前路由匹配信息
 *   - 错误对象(如果有)
 *   - 完整路径(fullPath)
 *   - hash值
 *   - 匹配的路由数组(matched)
 *   - 路径参数(params)
 *   - 路径名(pathname)
 *   - 查询参数(query)
 *   - 重定向标识(redirect)
 *   - 查询字符串(search)
 *
 * @remarks 使用useMemo优化性能，仅在fullPath变化时重新计算
 */
export function useRoute<
  T = unknown,
  Q extends Record<string, string> = Record<string, string>,
  P extends Record<string, string | string[]> = Record<string, string | string[]>,
>() {
  const matches = useMatches();
  const routes = matches.at(-1);
  const { hash, pathname, search } = useLocation();
  const fullPath = pathname + search + hash;
  const query = parseQuery(search) as Q;
  const error = useRouteError() as Error | null;
  return useMemo(
    () =>
      ({
        ...routes,
        error,
        fullPath,
        hash,
        matched: matches.slice(1) as unknown as Router.EnhancedRoute<T>[],
        params: routes ? (getParams(routes.params as Record<string, string>, routes.id) as P) : {},
        pathname,
        query,
        redirect: null,
        search,
      }) as unknown as Router.EnhancedRoute<T, Q, P>,
    [fullPath]
  );
}

/**
 * 获取上一个路由的上下文信息
 *
 * @returns 上一个路由的 Route 对象
 */
export function usePreviousRoute() {
  return useOutletContext<Router.EnhancedRoute>();
}
/**
 * 提取 catch-all 路由参数名（如 [...slug]）
 *
 * @param str 路由 id 字符串
 * @returns 捕获的参数名或 null
 */
function getCatchAllParam(str: string | undefined): string | null {
  if (!str) return null;
  const match = /\[\.\.\.(\w+)\]/.exec(str);
  return match ? match[1] : null;
}

/**
 * 处理路由参数，支持 catch-all 场景
 *
 * @param params 路由参数对象
 * @param id 路由 id
 * @returns 处理后的参数对象
 */
function getParams(
  params: Record<string, string> | undefined,
  id: string
): Record<string, string | string[]> | undefined {
  if (!params?.['*']) return params;
  const lastName = id.split('_').at(-1);
  const catchAllParam = getCatchAllParam(lastName);
  if (catchAllParam) {
    return { [catchAllParam]: params['*'].split('/') };
  }
  return params;
}
